package la.renzhen.rtpt.config.reload;

import la.renzhen.rtpt.config.ReloadStrategy;
import la.renzhen.rtpt.config.Reloadable;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PreDestroy;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static java.util.Objects.requireNonNull;

@Slf4j
public class PeriodicalReloadStrategy implements ReloadStrategy {

    private final long duration;
    private final TimeUnit timeUnit;
    private final Timer timer;
    private final Map<Reloadable, TimerTask> tasks;

    /**
     * @param duration time (in {@code timeUnit}) between reloads
     * @param timeUnit time unit to use
     */
    public PeriodicalReloadStrategy(long duration, TimeUnit timeUnit) {
        this.duration = duration;
        this.timeUnit = requireNonNull(timeUnit);
        tasks = Collections.synchronizedMap(new HashMap<Reloadable, TimerTask>());
        timer = new Timer();
    }

    @Override
    public void register(final Reloadable resource) {
        log.debug("Registering resource {} with reload time of {}{}", resource, duration, timeUnit);

        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                try {
                    resource.reload();
                } catch (Exception e) {
                    log.warn("Periodical resource reload failed. Will re-try at the next scheduled time.", e);
                }
            }
        };
        timerTask.run();//first running
        tasks.put(resource, timerTask);
        timer.schedule(timerTask, timeUnit.toMillis(duration), timeUnit.toMillis(duration));
    }

    @Override
    public void deregister(Reloadable resource) {
        log.debug("De-registering resource {}", resource);
        TimerTask timerTask = tasks.remove(resource);
        if (timerTask != null) {
            timerTask.cancel();
        }
    }

    @PreDestroy
    public void shutdown() {
        timer.cancel();
    }
}
